package com.meme.editor.gui.level;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Point;
import java.awt.SystemColor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.event.KeyListener;

import java.awt.geom.AffineTransform;

import java.util.HashSet;
import java.util.List;
import java.util.Vector;
import java.util.ListIterator;
import java.util.LinkedList;
import java.util.Collections; 
import java.util.Comparator;
import java.util.Collection;
import java.util.AbstractCollection;

import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import org.apache.log4j.Logger;

import com.meme.editor.Box2d;
import com.meme.editor.EntityObject;

import com.meme.editor.event.EntityChangedEvent;
import com.meme.editor.event.EntityObjectListener;
import com.meme.editor.gui.sprite2d.SharedData;
import com.meme.editor.gui.sprite2d.Tiled2dGridSelectedRegion;
import com.meme.editor.level.LevelFile;
import com.meme.editor.level.Actor;
import com.meme.editor.level.Appearance;
import com.meme.editor.level.Background2dAppearance;
import com.meme.editor.level.Box2dAppearance;
import com.meme.editor.level.CircleAppearance;
import com.meme.editor.level.Line2dAppearance;
import com.meme.editor.level.define.ActorTemplate;
import com.meme.editor.model.sprite2d.ModuleImage;
import com.meme.editor.model.sprite2d.ModuleRef;

import com.meme.editor.model.sprite2d.Tiled2dBackground;

import com.meme.editor.gui.sprite2d.Tiled2dBackgroundView;

import com.meme.editor.gui.PropertyEditDialog;

import com.meme.util.GuiUtil;

public class LevelMainView extends JPanel 
		implements EntityObjectListener, AncestorListener, 
				   MouseListener, MouseMotionListener, MouseWheelListener,
				   FocusListener, KeyListener
{
	static Logger logger = Logger.getLogger (LevelMainView.class);
	
	class ActorZOrderComparator<EntityObject> implements Comparator<EntityObject>
	{
		boolean mAscending;
		
		public ActorZOrderComparator (boolean zAscending)
		{
			mAscending = zAscending;
		}
		
		
		public int compare(EntityObject entity1, EntityObject entity2)
		{
			int z1 = ((Actor)entity1).getZOrder();
			int z2 = ((Actor)entity2).getZOrder();
			
			return mAscending ? z1 - z2 : z2 - z1;
		}

	}

	LevelTreeView mLevelTreeView;
	
	LevelFile mLevelFile;
	
	int mPlayfieldLeft;
	int mPlayfieldTop;
	
	int mScale = 1;
	
	static ActorSetupDialog mActorSetupDialog;
	static DefineFileSetupDialog mDefineFileSetupDialog;
	PropertyEditDialog mLevelPropertyEditDialog;
	
	Actor mSelectedActor;
	HashSet<Actor> mSelectedActorSet = new HashSet<Actor> ();
	
	Point mLastMousePos = new Point ();
	
	public LevelMainView (LevelFile levelFile)
	{
		mLevelFile = levelFile;
			
		mPlayfieldLeft = (int) ( -0.5f * (mLevelFile.getPlayfieldWidth()  * mScale) );
		mPlayfieldTop  = (int) ( -0.5f * (mLevelFile.getPlayfieldHeight() * mScale) );
		
		addAncestorListener (this);
		addMouseListener (this);
		addMouseMotionListener (this);
		addMouseWheelListener (this);
		
		setFocusable (true);
		addFocusListener (this);
		addKeyListener (this);
	}
	
	public void setLevelTreeView (LevelTreeView levelTreeView)
	{
		mLevelTreeView = levelTreeView;
	}
	
	
	List<EntityObject> _RenderList = new LinkedList<EntityObject> ();	
	@Override
	public void paintComponent (Graphics _g)
	{
		Graphics2D g = (Graphics2D)_g;
		
		updateSomeValues ( mScale, 0, 0);

		int viewWidth = getWidth ();
		int viewHeight = getHeight ();
		
		g.setColor ( SystemColor.window );
		g.fillRect(0, 0, viewWidth, viewHeight);
		
		int x = mPlayfieldLeft + viewWidth / 2;
		int y = mPlayfieldTop + viewHeight / 2;
		
		int fieldWidth = mLevelFile.getPlayfieldWidth ();
		int fieldHeight = mLevelFile.getPlayfieldHeight ();
		
		//
		g.setColor (SystemColor.textText);
		g.drawLine (x - 10, y, x + fieldWidth + 10, y);
		g.drawLine (x - 10, y + fieldHeight, x + fieldWidth + 10, y + fieldHeight);
		g.drawLine (x, y - 10, x, y + fieldHeight + 10 );
		g.drawLine (x + fieldWidth, y - 10, x + fieldWidth, y + fieldHeight + 10);
		
		//
		_RenderList.clear();
		_RenderList.addAll(mLevelFile.getActorList());
		Collections.sort (_RenderList, new ActorZOrderComparator<EntityObject> (true));
		
		Color regionSelectedHighLight = new Color (0x8000ff00, true);
		Color specailSelectedHighLight = new Color (0x800000ff, true);
		Box2d actorBoundingBox = new Box2d ();
		
		
		AffineTransform actorTransform = new AffineTransform ();
		Point actorPos = new Point ();
		Color posCrossColor = new Color (0xff0000); 
		
		for (EntityObject entity : _RenderList)
		{
			Actor actor = (Actor) entity;
			
			actorPos.setLocation(actor.getPosX(), actor.getPosY());
			_2dp (actorPos);
			
			//
			AffineTransform oldTransform = g.getTransform();
			
			double rotation = actor.getRotation();
			double scaleX = actor.getScaleX();
			double scaleY = actor.getScaleY();
			g.translate(actorPos.x, actorPos.y);
			g.rotate(rotation);
			g.scale(scaleX, scaleY);


			// draw little cross
			{
				g.setColor(posCrossColor);
				g.drawLine( - 3, 0,  + 3, 0);
				g.drawLine(0, - 3, 0, + 3);
			}
			
		
			boolean first = true;
			for (Appearance appearance : actor.getAppearanceList())
			{
				//appearance.show(g, actorPos.x, actorPos.y, actor.isFlipX (), actor.isFlipY (), mScale, 0);
				appearance.show(g, 0);
				
				if (first && actor.getSelectedState() > Actor.SelectedState_NotSelected)
				{
					Color oldColor = g.getColor();
					if (actor.getSelectedState() == Actor.SelectedState_RegionSelected)
						g.setColor (regionSelectedHighLight);
					else if (actor.getSelectedState() == Actor.SelectedState_SpecialSelected)
						g.setColor (specailSelectedHighLight);
					
					appearance.getBoundingBox (actorBoundingBox);
					//modifyBoundingBox (actorBoundingBox, actor.isFlipX (), actor.isFlipY (), mScale);
					//_2dBox (actorBoundingBox);
					//actorBoundingBox.shift(-actorPos.x, -actorPos.y);
					g.fillRect(actorBoundingBox.left,// + actorPos.x, 
							   actorBoundingBox.top,// + actorPos.y, 
							   actorBoundingBox.right - actorBoundingBox.left,
							   actorBoundingBox.bottom - actorBoundingBox.top );
					
					g.setColor (oldColor);
				}
				
				first = false;
			}
			
//			g.scale(1.0/scaleX, 1.0/scaleY);
//			g.rotate(-rotation);
//			g.translate(-actorPos.x, -actorPos.y);
			
			g.setTransform(oldTransform);

		}
		_RenderList.clear();
		
		//
		if (mRegionSelecting)
		{
			Color oldColor = g.getColor();
			g.setColor(new Color (0x00ff00));
			actorBoundingBox.set (mLeftButtonStartPoint.x, mLeftButtonStartPoint.y, mLastMousePos.x, mLastMousePos.y);
			g.drawRect(actorBoundingBox.left, actorBoundingBox.top, actorBoundingBox.right - actorBoundingBox.left, actorBoundingBox.bottom - actorBoundingBox.top);
			g.setColor (oldColor);
		}
		
	}
	
	protected void updateSomeValues (int newScale, int dx, int dy)
	{
		float ratioX;
		float ratioY;
		
		int fieldWidth = mLevelFile.getPlayfieldWidth ();
		int fieldHeight = mLevelFile.getPlayfieldHeight ();
		
		if ( fieldWidth != 0 )
			ratioX = (float) mPlayfieldLeft / (float) ( fieldWidth * mScale );
		else
			ratioX = - 0.5f;
		
		if ( fieldHeight != 0 )
			ratioY = (float) mPlayfieldTop / (float) ( fieldHeight * mScale );
		else
			ratioY = - 0.5f;
		
		mScale = newScale;
		

		mPlayfieldLeft = (int) ( ratioX * (fieldWidth  * mScale) );
		mPlayfieldTop  = (int) ( ratioY * (fieldHeight * mScale) );


		
		mPlayfieldLeft += dx;
		mPlayfieldTop  += dy;
		
		//
		if ( mPlayfieldLeft > 0 )
			mPlayfieldLeft = 0;
		if ( mPlayfieldTop > 0 )
			mPlayfieldTop = 0;
		int imgRight  = mPlayfieldLeft + (fieldWidth  * mScale);
		int imgBottom = mPlayfieldTop  + (fieldHeight * mScale);
		if ( imgRight < 0 )
			mPlayfieldLeft -= imgRight;
		if ( imgBottom < 0 )
			mPlayfieldTop -= imgBottom;

	}	
	
	void setPropertyDialogVisible (boolean visible)
	{
		boolean firstTime = mActorSetupDialog == null;
		if ( mActorSetupDialog == null )
		{
			mActorSetupDialog = new ActorSetupDialog (this);
		}
		
		if (visible)
		{
			mActorSetupDialog.setLevelMainView (this);
			mActorSetupDialog.setActor (mSelectedActor);
		}
		else
			mActorSetupDialog.setActor (null);
		
		mActorSetupDialog.pack();
		
		if (firstTime)
		{
            int screenWidth  = Toolkit.getDefaultToolkit().getScreenSize().width;			
            
		    Dimension size1 = mActorSetupDialog.getOwner().getSize();
		    Dimension size2 = mActorSetupDialog.getSize();
		    
		    int posX = mActorSetupDialog.getOwner().getLocation().x + size1.width;
		    if (posX > screenWidth - size2.width)
		    	posX = screenWidth - size2.width;
		    
		    mActorSetupDialog.setLocation ( 
		    		posX,
					mActorSetupDialog.getOwner().getLocation().y + ( size1.height - size2.height ) / 2 );
		}
			
		mActorSetupDialog.setVisible ( visible );
	}
	
	void updatePropertyDialog ()
	{
		if (mActorSetupDialog.isVisible())
		{
			setPropertyDialogVisible (true);
		}
	}
	
	void togglePropertyDialogVisible ()
	{
		setPropertyDialogVisible (! mActorSetupDialog.isVisible());
	}
	
	void setDefineFileSetupDialogVisible (boolean visible)
	{
		if (mDefineFileSetupDialog == null)
		{
			mDefineFileSetupDialog = new DefineFileSetupDialog (this);
			
		    Dimension size1 = mDefineFileSetupDialog.getOwner().getSize();
		    Dimension size2 = mDefineFileSetupDialog.getSize();
		    mDefineFileSetupDialog.setLocation ( 
		    		mDefineFileSetupDialog.getOwner().getLocation().x < 0 ? 0 : mDefineFileSetupDialog.getOwner().getLocation().x,
		    		mDefineFileSetupDialog.getOwner().getLocation().y + ( size1.height - size2.height ) / 2 );
		}
		
		if (visible)
		{
			mDefineFileSetupDialog.setLevelMainView (this);
			mDefineFileSetupDialog.setDefinefile (mLevelFile.getDefineFile());
		}
		else
			mDefineFileSetupDialog.setDefinefile (null);		
		
		mDefineFileSetupDialog.pack();
		mDefineFileSetupDialog.setVisible ( visible );
	}
	
	
	void toogleDefineFileSetupDialogVisible ()
	{
		setDefineFileSetupDialogVisible (!mDefineFileSetupDialog.isVisible());
	}
	
	void setLevelPropertyEditDialogVisible (boolean visible)
	{
		if (mLevelPropertyEditDialog == null)
		{
			mLevelPropertyEditDialog = new PropertyEditDialog (GuiUtil.getTopFrame (this));
			mLevelPropertyEditDialog.setDataSource (mLevelFile);
		}
		
		mLevelPropertyEditDialog.pack();
	    Dimension size1 = mLevelPropertyEditDialog.getOwner().getSize();
	    Dimension size2 = mLevelPropertyEditDialog.getSize();
		mLevelPropertyEditDialog.setLocation ( 
				mLevelPropertyEditDialog.getOwner().getLocation().x + ( size1.width - size2.width ) / 2,
				mLevelPropertyEditDialog.getOwner().getLocation().y + ( size1.height - size2.height ) / 2 );
		mLevelPropertyEditDialog.setVisible ( visible );
	}
	
	void toggleLevelPropertyEditDialogVisible ()
	{
		setLevelPropertyEditDialogVisible (!mLevelPropertyEditDialog.isVisible());
	}

	
	void modifyBoundingBox (Box2d box, boolean flipX, boolean flipY, int scale)
	{
		if (flipX)
		{
			int temp = box.left;
			box.left = -box.right;
			box.right = -temp;
		}
		
		if (flipY)
		{
			int temp = box.top;
			box.top = -box.bottom;
			box.bottom = -temp;
		}
		
		if (scale != 1)
		{
			box.scale (scale);
		}
	}
	
	protected Point lp2dp (Point lp)
	{
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();
		
		Point dp = new Point ();
		
		dp.x = ( mPlayfieldLeft + lp.x * mScale ) + viewWidth / 2;
		dp.y = ( mPlayfieldTop  + lp.y * mScale ) + viewHeight / 2;
		
		return dp;
	}
	
	protected void _2dp (Point lp)
	{
		Point dp = lp2dp (lp);
		lp.setLocation (dp);
	}
	
	protected Point dp2lp (Point dp)
	{
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();

		Point lp = new Point ();
		
		lp.x = ( dp.x - viewWidth  / 2 - mPlayfieldLeft ) / mScale;
		lp.y = ( dp.y - viewHeight / 2 - mPlayfieldTop  ) / mScale;
		
		return lp;
	}
	
	protected void _2lp (Point dp)
	{
		Point lp = dp2lp (dp);
		dp.setLocation (lp);
	}

	protected Dimension ds2ls (Dimension size)
	{
		return new Dimension (size.width / mScale, size.height / mScale);
	}
	
	protected void _2ls (Dimension size)
	{
		Dimension ls = ds2ls (size);
		size.setSize(ls);
	}
	protected Dimension ls2ds (Dimension size)
	{
		return new Dimension (size.width * mScale, size.height * mScale);
	}
	
	protected void _2ds (Dimension size)
	{
		Dimension ds = ls2ds (size);
		size.setSize(ds);
	}
	
	protected void _2dBox (Box2d box)
	{
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();
		
		box.left = ( mPlayfieldLeft + box.left * mScale ) + viewWidth / 2;
		box.top = ( mPlayfieldTop  + box.top * mScale ) + viewHeight / 2;
		box.right = ( mPlayfieldLeft + box.right * mScale ) + viewWidth / 2;
		box.bottom = ( mPlayfieldTop  + box.bottom * mScale ) + viewHeight / 2;

	}
		
	
//--------------------------------------------------------------------------
// as a PropertyManagerListener
//--------------------------------------------------------------------------

	@Override 
	public void notifyEntityChanged (EntityChangedEvent e)
	{
		
		repaint ();
	}
	
//--------------------------------------------------------------------------
// as a AncestorListener
//--------------------------------------------------------------------------

	boolean _PropertyEditDialogVisibleStateBeforeBeingRemoved = false;
	boolean _DefineFileSetupDialogVisibleStateBeforeBeingRemoved = false;
	boolean _LevelPropertyEditDialogVisibleStateBeforeBeingRemoved = false;

	
	@Override
	public void ancestorAdded(AncestorEvent event) 
	{
		setPropertyDialogVisible (_PropertyEditDialogVisibleStateBeforeBeingRemoved);
		setDefineFileSetupDialogVisible (_DefineFileSetupDialogVisibleStateBeforeBeingRemoved);
		setLevelPropertyEditDialogVisible (_LevelPropertyEditDialogVisibleStateBeforeBeingRemoved);
		
		mLevelFile.addEntityObjectListener (this);
	}

	@Override
	public void ancestorRemoved(AncestorEvent event)  
	{
		_PropertyEditDialogVisibleStateBeforeBeingRemoved = mActorSetupDialog.isVisible();
		_DefineFileSetupDialogVisibleStateBeforeBeingRemoved = mDefineFileSetupDialog.isVisible();
		_LevelPropertyEditDialogVisibleStateBeforeBeingRemoved = mLevelPropertyEditDialog.isVisible();
		
		setPropertyDialogVisible (false);
		setDefineFileSetupDialogVisible (false);	
		setLevelPropertyEditDialogVisible (false);	
		
		mLevelFile.removeEntityObjectListener (this);
	}
	
	@Override
	public void ancestorMoved(AncestorEvent event) 
	{
		
	}	
//===========================================================================
//
//===========================================================================
	
	boolean mMovingPlayfield = false;	
	int mLastPlayfieldX;
	int mLastPlayfieldY;
	Point mRightButtonStartPoint = new Point ();

	void startMovingPlayfield (Point startDevice)
	{
		mMovingPlayfield = true;
		mRightButtonStartPoint.setLocation(startDevice);
		
		mLastPlayfieldX = mPlayfieldLeft;
		mLastPlayfieldY = mPlayfieldTop;
	}
	
	void updateMovingPlayfield (Point point)
	{
		  updateSomeValues (mScale, 
				  mLastPlayfieldX + point.x - mRightButtonStartPoint.x - mPlayfieldLeft,
				  mLastPlayfieldY + point.y - mRightButtonStartPoint.y - mPlayfieldTop
				  );
		  
			
		  setCursor (Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		  
		  repaint ();
	}
	
	void endMovingPlayfield ()
	{
		mMovingPlayfield = false;
		setCursor (Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}	
	
//-------
	
	void deleteActor (Actor actor)
	{
		if (actor.getSelectedState() > Actor.SelectedState_NotSelected)
		{
			mSelectedActorSet.remove (actor);
			
			if (actor == mSelectedActor)
			{
				if (mSelectedActorSet.size() > 0)
					mSelectedActor = mSelectedActorSet.iterator().next();
				else
					mSelectedActor = null;
			}
			
			actor.setSelectedState (Actor.SelectedState_NotSelected);
		}
		
		//mLevelFile.removeChild (actor);
		mLevelFile.removeActor (actor);
	}
	
	public void synchronizeTreeView ()
	{
		if (mLevelTreeView != null)
		{
			int[] indices = new int[mSelectedActorSet.size()];
			int i = 0;
			for (Actor a : mSelectedActorSet)
				indices [i ++] = a.getIndex();
			
			mLevelTreeView.setListSelectedIndices (indices);
		}
	}
	
	public void setSelectedActor (Actor actor)
	{
		Vector<Actor> actors = new Vector <Actor> ();
		actors.add (actor);
		setSelectedActors (actors, true);
	}
	
	public void synchronizedWithTreeView (List<Actor> actorList)
	{
		setSelectedActors (actorList, false);
	}
	
	public void setSelectedActors (List<Actor> actorList, boolean updateTreeView)
	{
		boolean same = true;

		HashSet<Actor> actorSet = new HashSet<Actor> ();
		actorSet.addAll (actorList);
		
		for (Actor a : mSelectedActorSet)
		{
			if (! actorSet.contains(a))
			{
				same = false;
				break;
			}
		}
		
		
		for (Actor a : actorList)
		{
			if (! mSelectedActorSet.contains(a))
			{
				same = false;
				break;
			}
		}
		
		if (same)
			return;
		
		Actor oldSelectedActor = mSelectedActor;
		
		// clear old ones
		clearSelectedActors ();
		
		//
		mSelectedActorSet.addAll (actorList);

		if (oldSelectedActor != null && mSelectedActorSet.contains(oldSelectedActor))
			mSelectedActor = oldSelectedActor;
		else if (actorList.size() > 0)
			mSelectedActor = actorList.get (0);
		
		updatePropertyDialog ();

		//
		setSelectedActorSelectedState ();
		
		if (updateTreeView)
			synchronizeTreeView ();
		
		repaint ();
	}
	
	void clearSelectedActors ()
	{
		if (mSelectedActor != null)
			mSelectedActor.setSelectedState (Actor.SelectedState_NotSelected);
		mSelectedActor = null;
		for (Actor a : mSelectedActorSet)
			a.setSelectedState (Actor.SelectedState_NotSelected);
		mSelectedActorSet.clear();	
	}
	
	void setSelectedActorSelectedState ()
	{
		for (Actor a : mSelectedActorSet)
			a.setSelectedState (Actor.SelectedState_RegionSelected);
		
		if (mSelectedActor != null)
			mSelectedActor.setSelectedState (Actor.SelectedState_SpecialSelected);
	}
	
	void getSelectedActorsByPoint (Point logicPoint, boolean appendNewOnes)
	{
		Box2d logicRegionBox2d = new Box2d (logicPoint.x, logicPoint.y, logicPoint.x, logicPoint.y);
		getSelectedActors (logicRegionBox2d, false, appendNewOnes);
		
		updatePropertyDialog ();
		repaint ();
	}
	
	void getSelectedActorsByRegion (Box2d logicRegionBox2d, boolean appendNewOnes)
	{
		getSelectedActors (logicRegionBox2d, true, appendNewOnes);
		
		updatePropertyDialog ();
		repaint ();
	}
	
	void getSelectedActors (Box2d logicRegionBox2d, boolean regionSelection, boolean appendNewOnes)
	{
		Actor lastSelectedActor = mSelectedActor;

		if (! appendNewOnes)
			clearSelectedActors ();
		
		// find new ones
		List<Actor> actorList;
		
		if (regionSelection)
			actorList = getActorsInCollectionInRegion2 (mLevelFile.getActorList(), logicRegionBox2d ,false);
		else
			actorList = getActorsInCollectionInRegion (mLevelFile.getActorList(), logicRegionBox2d ,false);
			
		if (actorList.size() == 0)
		{
			synchronizeTreeView ();
			return;
		}
		
		int lastIndex = -1;
		if (lastSelectedActor != null)
			lastIndex = actorList.indexOf(lastSelectedActor);
		
		if (lastIndex >= 0 && lastIndex < actorList.size() - 1)
		{
			actorList.addAll (actorList.subList(0, lastIndex + 1));
			while (lastIndex-- >= 0)
				actorList.remove (0);
		}
		
		if (appendNewOnes)
		{
			if (regionSelection)
			{
				mSelectedActorSet.addAll (actorList);
				if (mSelectedActor == null)
					mSelectedActor = actorList.get (0);
			}
			else
			{
				Actor newSelected = actorList.get (0);
				if (newSelected.getSelectedState() > Actor.SelectedState_NotSelected)
				{
					newSelected.setSelectedState (Actor.SelectedState_NotSelected);
					mSelectedActorSet.remove (newSelected);
					
					if (mSelectedActor == newSelected)
					{
						if (mSelectedActorSet.size() > 0)
							mSelectedActor = mSelectedActorSet.iterator().next();
						else
							mSelectedActor = null;
					}
				}
				else
				{
					if (mSelectedActor == null)
					{
						logger.assertLog(mSelectedActorSet.size () == 0, "somewhere wrong!");
						
						//mSelectedActor = newSelected;
					}
					mSelectedActor = newSelected;
					
					mSelectedActorSet.add (newSelected);
				}
			}
		}
		else
		{
			mSelectedActor = actorList.get (0);
			if (regionSelection)
				mSelectedActorSet.addAll (actorList);
			else
				mSelectedActorSet.add (mSelectedActor);			
		}
		
		//
		setSelectedActorSelectedState ();
		
		//
		synchronizeTreeView ();

	}
	
	List<Actor> getActorsIntersectWithPoint (Point logicPoint, boolean includeBackground2d)
	{
		Box2d logicRegionBox2d = new Box2d (logicPoint.x, logicPoint.y, logicPoint.x, logicPoint.y);
		return getActorsInCollectionInRegion (mLevelFile.getActorList(), logicRegionBox2d, includeBackground2d);
	}
	
	
	
	List<Actor> getActorsInCollectionInRegion (Collection collection, Box2d logicRegionBox2d, boolean includeBackground2d)
	{
		List<Actor> actorList = new LinkedList <Actor> ();
		
//		Box2d actorBoundingBox = new Box2d ();
//		for (Object entity : collection ) //mLevelFile.getActorList())
//		{
//			Actor actor = (Actor) entity;
//			
//			AffineTransform actorTransform = new AffineTransform ();
//	
//			if (actor.getAppearanceList().size() > 0)
//			{
//				Appearance appearance = actor.getAppearanceList().get (0);
//				
//				if (  includeBackground2d 
//					|| (! (appearance instanceof Background2dAppearance) )
//					)
//				{
//					appearance.getBoundingBox (actorBoundingBox);
//					//modifyBoundingBox (actorBoundingBox, actor.isFlipX (), actor.isFlipY (), 1);
//					//actorBoundingBox.shift (actor.getPosX(), actor.getPosY());
//
//					if ( actorBoundingBox.intersectWith(logicRegionBox2d) )
//					{
//						actorList.add (actor);
//					}
//				}
//				
//			}
//		}
		
		// current, noly support point selecting
		Point regionPoint = new Point (
				(logicRegionBox2d.left + logicRegionBox2d.right ) / 2,				
				(logicRegionBox2d.top  + logicRegionBox2d.bottom) / 2
				);
		Point regionPointInActorSpace = new Point ();
		Point actorPos = new Point ();
		Box2d actorBoundingBox = new Box2d ();
		for (Object entity : collection ) //mLevelFile.getActorList())
		{
			Actor actor = (Actor) entity;
			
			actorPos.setLocation(actor.getPosX(), actor.getPosY());
			//_2dp (actorPos);

			double rotation = actor.getRotation();
			double scaleX = actor.getScaleX();
			double scaleY = actor.getScaleY();
			
			AffineTransform actorTransform = new AffineTransform ();
			actorTransform.translate(actorPos.x, actorPos.y);
			actorTransform.rotate(rotation);
			actorTransform.scale(scaleX, scaleY);
			
			try{
			actorTransform.invert();
			}catch(Exception e){}

	
			if (actor.getAppearanceList().size() > 0)
			{
				Appearance appearance = actor.getAppearanceList().get (0);
				
				if (  includeBackground2d 
					|| (! (appearance instanceof Background2dAppearance) )
					)
				{
					appearance.getBoundingBox (actorBoundingBox);
					//modifyBoundingBox (actorBoundingBox, actor.isFlipX (), actor.isFlipY (), 1);
					//actorBoundingBox.shift (-actor.getPosX(), -actor.getPosY());

					actorTransform.transform(regionPoint, regionPointInActorSpace);
					if ( actorBoundingBox.containsPoint(regionPointInActorSpace.x, regionPointInActorSpace.y) )
					{
						actorList.add (actor);
					}
				}
				
			}
		}

		
		Collections.sort (actorList, new ActorZOrderComparator<EntityObject> (false));
		return actorList;
	}
	
	List<Actor> getActorsInCollectionInRegion2 (Collection collection, Box2d logicRegionBox2d, boolean includeBackground2d)
	{
		List<Actor> actorList = new LinkedList <Actor> ();
		
//		Box2d actorBoundingBox = new Box2d ();
//		for (Object entity : collection ) //mLevelFile.getActorList())
//		{
//			Actor actor = (Actor) entity;
//			
//			AffineTransform actorTransform = new AffineTransform ();
//	
//			if (actor.getAppearanceList().size() > 0)
//			{
//				Appearance appearance = actor.getAppearanceList().get (0);
//				
//				if (  includeBackground2d 
//					|| (! (appearance instanceof Background2dAppearance) )
//					)
//				{
//					appearance.getBoundingBox (actorBoundingBox);
//					//modifyBoundingBox (actorBoundingBox, actor.isFlipX (), actor.isFlipY (), 1);
//					//actorBoundingBox.shift (actor.getPosX(), actor.getPosY());
//
//					if ( actorBoundingBox.intersectWith(logicRegionBox2d) )
//					{
//						actorList.add (actor);
//					}
//				}
//				
//			}
//		}
		

		for (Object entity : collection ) //mLevelFile.getActorList())
		{
			Actor actor = (Actor) entity;
			
			if (actor.getAppearanceList().size() > 0)
			{
				Appearance appearance = actor.getAppearanceList().get (0);
				
				if (  includeBackground2d 
					|| (! (appearance instanceof Background2dAppearance) )
					)
				{
					if (logicRegionBox2d.containsPoint(actor.getPosX(), actor.getPosY()))
					{
						actorList.add (actor);
					}
				}
			}
		}

		
		Collections.sort (actorList, new ActorZOrderComparator<EntityObject> (false));
		return actorList;
	}
	
//-------
	
	boolean mCreatingNewActor;
	Actor mTempNewActor;
	int mLastTempNewActorPosX;
	int mLastTempNewActorPosY;
	Point mTempNewStartPoint = new Point ();		
	
	void startCreatingNewActor (ActorTemplate actorTemplate)
	{
		cancelCreatingNewActor ();
		
		mCreatingNewActor = true;
		mTempNewActor = mLevelFile.createActorFromTemplate (actorTemplate);
		
		Point lp = dp2lp (mLastMousePos);
		mTempNewActor.setPosition (lp.x, lp.y);
		
		mTempNewStartPoint.setLocation(mLastMousePos);
		mLastTempNewActorPosX = mTempNewActor.getPosX();
		mLastTempNewActorPosY = mTempNewActor.getPosY();	
		
		mLevelFile.commitModifications();
		repaint ();
	}
	
	void updateCreatingNewActor (Point devicePoint)
	{
		int dx = devicePoint.x - mTempNewStartPoint.x;
		int dy = devicePoint.y - mTempNewStartPoint.y;
		Dimension dSize = new Dimension (dx, dy);
		_2ls (dSize);
		
		mTempNewActor.setPosition(mLastTempNewActorPosX + dSize.width, 
				                  mLastTempNewActorPosY + dSize.height);
		
		repaint ();
	}
	
	void endCreatingNewActor ()
	{
		//mLevelFile.addChild (mTempNewActor);
		setSelectedActor (mTempNewActor);
		
		mTempNewActor = null;
		mCreatingNewActor = false;
		
		mLevelFile.commitModifications();
		
		repaint ();
	}
	
	void cancelCreatingNewActor ()
	{
		if (mTempNewActor != null)
		{
			//mLevelFile.removeChild (mTempNewActor);
			mLevelFile.removeActor (mTempNewActor);
			mTempNewActor = null;
			mCreatingNewActor = false;
			
			mLevelFile.commitModifications();
		}
		
		repaint ();
	}
	
//--------
	
	boolean mMovingSelectedActors;
	int mLastSelectedActorPosX;
	int mLastSelectedActorPosY;
	Point mLeftButtonStartPoint = new Point ();	
	
	
	boolean isPointOnSelectedActors (Point devicePoint)
	{
		Point logicPoint = dp2lp (devicePoint);
		Box2d logicRegionBox2d = new Box2d (logicPoint.x, logicPoint.y, logicPoint.x, logicPoint.y);
		List<Actor> list = getActorsInCollectionInRegion ( mSelectedActorSet, logicRegionBox2d, false);
		return list.size() > 0;
	}
	
	void startMovingSelectedActors (Point devicePoint)
	{
		if (mSelectedActor == null)
			return;
		
		mMovingSelectedActors = true;
		mLeftButtonStartPoint.setLocation(devicePoint);
		mLastSelectedActorPosX = mSelectedActor.getPosX();
		mLastSelectedActorPosY = mSelectedActor.getPosY();
	}
	
	void updateMovingSelectedActors (Point devicePoint)
	{
		int dx = devicePoint.x - mLeftButtonStartPoint.x;
		int dy = devicePoint.y - mLeftButtonStartPoint.y;
		Dimension dSize = new Dimension (dx, dy);
		_2ls (dSize);
		
		int lastX = mSelectedActor.getPosX();
		int lastY = mSelectedActor.getPosY();
		int newX = mLastSelectedActorPosX + dSize.width;
		int newY = mLastSelectedActorPosY + dSize.height;
		dx = newX - lastX;
		dy = newY - lastY;
		
		for (Actor a : mSelectedActorSet)
		{
			a.setPosition (a.getPosX() + dx, a.getPosY() + dy);
			a.commitModifications ();
		}
	
		repaint ();
	}
	
	void endMovingSelectedActors ()
	{
		mMovingSelectedActors = false;
	}
	
//-------
	
	boolean mRegionSelecting;
	Box2d   mLastRegionSelectBox;
	
	void startRegionSelection (Point devicePoint)
	{
		mRegionSelecting = true;
		
		mLeftButtonStartPoint.setLocation(devicePoint);
		
		Point lp = dp2lp (mLeftButtonStartPoint);
		mLastRegionSelectBox = new Box2d (lp.x, lp.y, lp.x, lp.y);
		getSelectedActorsByRegion (mLastRegionSelectBox, false);
	}
	
	void updateRegionSelection (Point devicePoint)
	{
		Point lp1 = dp2lp (mLeftButtonStartPoint);
		Point lp2 = dp2lp (devicePoint);
		Box2d logicRegionBox2d = new Box2d (lp1.x, lp1.y, lp2.x, lp2.y);
		

		getSelectedActorsByRegion (logicRegionBox2d, logicRegionBox2d.containsBox (mLastRegionSelectBox));
		
		mLastRegionSelectBox = logicRegionBox2d;
	}
	
	void endRegionSelection ()
	{
		mRegionSelecting = false;
		mLastRegionSelectBox = null;
	}
	
//-----
	
	boolean mAssigningBox2dAppearance;
	int mBeingAssignedBox2dAppearanceOrderID;
	
	int getBeingAssignedBox2dAppearanceOrderID (java.awt.event.MouseEvent e)
	{
		/*
		int orderID = -1;
		
		
		
		if (e.isAltDown())
		{
			if (e.isShiftDown() && e.isControlDown())
			{
				orderID = 3;
			}
			else if (e.isControlDown())
			{
				orderID = 2;
			}
			else if (e.isShiftDown())
			{
				orderID = 1;
			}
			else
			{
				orderID = 0;
			}
		}
		
		return orderID;
		*/
		
		return mBeingAssignedBox2dAppearanceOrderID;
	}
	
	void startAssigningBox2dAppearance (Point devicePoint, int box2dAppearanceOrderID)
	{
		mAssigningBox2dAppearance = true;
		
		mBeingAssignedBox2dAppearanceOrderID = box2dAppearanceOrderID;
		
		mLeftButtonStartPoint.setLocation(devicePoint);
	}
	
	void updateAssigningBox2dAppearance (Point devicePoint)
	{
		Point point2 = dp2lp (devicePoint);
		Point point1 = dp2lp (mLeftButtonStartPoint);
		
		int left, top, right, bottom;
		
		if (point1.x < point2.x)
		{
			left = point1.x;
			right = point2.x;
		}
		else
		{
			left = point2.x;
			right = point1.x;
		}
		
		if (point1.y < point2.y)
		{
			top = point1.y;
			bottom = point2.y;
		}
		else
		{
			top = point2.y;
			bottom = point1.y;
		}
		
		boolean modified = false;
		for (Actor actor : mSelectedActorSet)
		{
			int index = 0;
			for (Appearance appearance : actor.getAppearanceList())
			{
				if ( index == mBeingAssignedBox2dAppearanceOrderID )
				{
					if (appearance.isGeometryAppearance())
					{
						if (appearance instanceof Box2dAppearance)
						{
							Box2d box2dRegion = new Box2d ();
							box2dRegion.set(left, top, right, bottom);
							box2dRegion.shift(- actor.getPosX(), - actor.getPosY());
							( (Box2dAppearance) appearance ).setRegionValues(box2dRegion.left, box2dRegion.top, box2dRegion.right, box2dRegion.bottom);
						}
						else if (appearance instanceof CircleAppearance)
						{
							int radius;
							if (right - left < bottom - top)
							{
								radius = (bottom - top);
							}
							else
							{
								radius = (right - left);
							}
							
							( (CircleAppearance) appearance ).SetCenterX ( point1.x - actor.getPosX());
							( (CircleAppearance) appearance ).SetCenterY ( point1.y - actor.getPosY());
							( (CircleAppearance) appearance ).SetRadius ( radius );
						}
						else if (appearance instanceof Line2dAppearance)
						{
							point1.x -= actor.getPosX();
							point2.x -= actor.getPosX();
							point1.y -= actor.getPosY();
							point2.y -= actor.getPosY();

							( (Line2dAppearance) appearance ).SetPoint1X (point1.x);
							( (Line2dAppearance) appearance ).SetPoint1Y (point1.y);
							( (Line2dAppearance) appearance ).SetPoint2X (point2.x);
							( (Line2dAppearance) appearance ).SetPoint2Y (point2.y);
						}
						
						modified = true;
					}
					
					break;
				}
				
				++ index;
			}
		}
		
		if (modified)
			mLevelFile.commitModifications();
	}
	
	void endAssigningBox2dAppearance ()
	{
		mAssigningBox2dAppearance = false;
	}

//--------------------------------------------------------------------------
// as a MouseListener
//--------------------------------------------------------------------------

	
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseClicked(java.awt.event.MouseEvent e)
	{
//		if (e.getButton () == e.BUTTON3)
//		{
//			setSelectedModule ( getModuleAtPoint ( e.getPoint() ) );
//			
//			if ( mReadOnly )
//			{
//				JPopupMenu popMenu = new JPopupMenu();
//				AbstractAction action = new ReplaceModuleInPartAction (); 
//				popMenu.add ( action );
//				
//
//				ModuleRef part = SharedData.sSelectedModuleRef;
//				
//				action.setEnabled(part != null && part.checkReferability(mSelectedModule));
//				
//				popMenu.show(this, e.getX(), e.getY());
//			}
//		}
	}
	
	boolean mProcessActorSelectingWhenMosueReleased;
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mousePressed(java.awt.event.MouseEvent e)
	{
		requestFocusInWindow(true);
		
		//PureModule module = getModuleAtPoint ( e.getPoint() );
		
		if (e.getButton () == e.BUTTON1)
		{
			//updateCursor (e.getPoint(), true);
			
			//
			if (mCreatingNewActor)
				return;
			
			// change box2d appearances
			if (e.isAltDown())
			{
				startAssigningBox2dAppearance (e.getPoint(), getBeingAssignedBox2dAppearanceOrderID (e));
				return;
			}
			
			List<Actor> actorList = getActorsIntersectWithPoint ( dp2lp (e.getPoint()), false);
			if (actorList.size() == 0)
			{
				startRegionSelection (e.getPoint());
				return;
			}
			
			mProcessActorSelectingWhenMosueReleased = true;
			if (! isPointOnSelectedActors (e.getPoint()))
			{
				mProcessActorSelectingWhenMosueReleased = false;
				getSelectedActorsByPoint (dp2lp (e.getPoint()), e.isControlDown());
			}
		
			// move actors
			if (isPointOnSelectedActors (e.getPoint()))
			{
				startMovingSelectedActors (e.getPoint());
				return;
			}
			
			
			// select actors
			//mLeftButtonStartPoint.setLocation(e.getPoint());

			
		}
		  
		if (e.getButton () == e.BUTTON3)
		{			
			if (mCreatingNewActor)
				cancelCreatingNewActor ();
			
			startMovingPlayfield (e.getPoint());
			  
			//cancelTempModule ();
		}
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseReleased(java.awt.event.MouseEvent e)
	{
		if (e.getButton () == e.BUTTON1)
		{
			// create new actor
			if (mCreatingNewActor)
			{
				updateCreatingNewActor (e.getPoint());
				endCreatingNewActor ();
				return;
			}
			
			if (mAssigningBox2dAppearance)
			{
				updateAssigningBox2dAppearance (e.getPoint());
				endAssigningBox2dAppearance ();
				
				return;
			}
			
			if (mRegionSelecting)
			{
				updateRegionSelection (e.getPoint());
				endRegionSelection ();
				
				return;
			}
			
			// move actor
			if (mMovingSelectedActors)
			{
				updateMovingSelectedActors (e.getPoint());
				endMovingSelectedActors ();
				
				if ( ! mProcessActorSelectingWhenMosueReleased)
					return;
			}
			
			
			if (mProcessActorSelectingWhenMosueReleased 
					// && mSelectedActorSet.size() <= 1 // 
					)
			{
				getSelectedActorsByPoint (dp2lp (e.getPoint()), e.isControlDown());
			}
		}
		  
		if (e.getButton () == e.BUTTON3)
		{			
			if (mMovingPlayfield)
			{
				updateMovingPlayfield (e.getPoint());
				endMovingPlayfield ();
			}

			//cancelTempModule ();
		}
		
		//updateCursor (e.getPoint(), false);		  
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseEntered(java.awt.event.MouseEvent e)
	{
		
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseExited(java.awt.event.MouseEvent e)
	{
		
	}
	  
//--------------------------------------------------------------------------
// as a MouseMotionListener 
//--------------------------------------------------------------------------

	// Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	public void mouseDragged(java.awt.event.MouseEvent e)
	{
		mProcessActorSelectingWhenMosueReleased = false;
		
		mLastMousePos.setLocation (e.getPoint());
		
		if (mCreatingNewActor)
		{
			updateCreatingNewActor (e.getPoint());
			return;
		}
		
		if (mAssigningBox2dAppearance)
			updateAssigningBox2dAppearance (e.getPoint());
		
		if (mRegionSelecting)
			updateRegionSelection (e.getPoint());
		
		if (mMovingPlayfield)
			updateMovingPlayfield (e.getPoint());
		
		if (mMovingSelectedActors)
			updateMovingSelectedActors (e.getPoint());
		
//		if (mResizingSelectedModule)
//			updateResizingSelectedModule (e.getPoint());
//		  
//		if ( mMovingSelectedModule )
//			updateMovingSelectedModule (e.getPoint());
//		  
//		if ( mCreatingNewModule )
//		{
//			updateTempModule (e.getPoint());
//			repaint ();
//		}
	}
	  
	  // Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	public void mouseMoved(java.awt.event.MouseEvent e)
	{
		mLastMousePos.setLocation (e.getPoint());
		
		if (mCreatingNewActor)
		{
			updateCreatingNewActor (e.getPoint());
			return;
		}
		
		//updateCursor (e.getPoint(), false);
	}
	  
//--------------------------------------------------------------------------
// as a MouseWheelListener
//--------------------------------------------------------------------------

	// Method descriptor #8 (Ljava/awt/event/MouseWheelEvent;)V
	public void mouseWheelMoved(java.awt.event.MouseWheelEvent event)
	{
//		float scale = (float)mScale + ( event.getWheelRotation () > 0 ? 1 : -1 );
//		
//		if ( scale < MinScale)
//			scale = MinScale;
//		if (scale > MaxScale)
//			scale = MaxScale;
//
//		updateSomeValues ((int)scale, 0, 0);
//		repaint ();
	}
	  
	
//--------------------------------------------------------------------------
// as a KeyListener
//--------------------------------------------------------------------------

	  // Method descriptor #10 (Ljava/awt/event/KeyEvent;)V
	  public void keyTyped(java.awt.event.KeyEvent e)
	  {
		  
	  }
	  
	  // Method descriptor #10 (Ljava/awt/event/KeyEvent;)V
	  public void keyPressed(java.awt.event.KeyEvent e)
	  {
		  switch (e.getKeyCode())
		  {
		  case java.awt.event.KeyEvent.VK_F1:
		  {
			  togglePropertyDialogVisible ();
			  break;
		  }
		  case java.awt.event.KeyEvent.VK_F2:
		  {
			  toogleDefineFileSetupDialogVisible ();
			  break;
		  }
		  case java.awt.event.KeyEvent.VK_F3:
		  {
			  toggleLevelPropertyEditDialogVisible ();
			  break;
		  }
		  case java.awt.event.KeyEvent.VK_ALT:
		  {
			  mBeingAssignedBox2dAppearanceOrderID = 0;
			  break;
		  }
		  case java.awt.event.KeyEvent.VK_1:
		  case java.awt.event.KeyEvent.VK_2:
		  case java.awt.event.KeyEvent.VK_3:
		  case java.awt.event.KeyEvent.VK_4:
		  case java.awt.event.KeyEvent.VK_5:
		  case java.awt.event.KeyEvent.VK_6:
		  case java.awt.event.KeyEvent.VK_7:
		  case java.awt.event.KeyEvent.VK_8:
		  case java.awt.event.KeyEvent.VK_9:
		  {
			  if (e.isAltDown()) // alt + n: assign appearance
			  {
				  mBeingAssignedBox2dAppearanceOrderID = e.getKeyChar() - '1' + 1;
				  return;
			  }
			  
			  int layerID = e.getKeyChar() - '1';
			  
			  HashSet<Tiled2dBackground> tiled2dBackgrounds = new HashSet<Tiled2dBackground> ();
			  for (EntityObject entity : mLevelFile.getActorList())
			  {
				  Actor actor = (Actor) entity;
				  for (Appearance appearance : actor.getAppearanceList())
				  {
					  if (appearance instanceof Background2dAppearance)
					  {
						  Tiled2dBackground background = ((Background2dAppearance)appearance).getTiled2dBackground();
						  tiled2dBackgrounds.add(background);
					  }
				  }
			  }
			  
			  for (Tiled2dBackground background : tiled2dBackgrounds)
			  {
				  Tiled2dBackgroundView.toggleTiledBackgroundLayerVisibility(background, layerID);				  
			  }
			  
			  if (tiled2dBackgrounds.size() > 0)
				  repaint ();
			  
			  break;
		  }		  
		  case java.awt.event.KeyEvent.VK_DELETE:
		  {
			  List <Actor> actorList = new LinkedList <Actor> ();
			  actorList.addAll (mSelectedActorSet);
			  for (Actor a : actorList)
				  deleteActor (a);
			  
			  mLevelFile.commitModifications();
			  
			  break;
		  }
		  case java.awt.event.KeyEvent.VK_C:
		  {
			  List<Actor> fromActors = new Vector <Actor> (mSelectedActorSet);
			  List<Actor> newActors = new Vector <Actor> (mSelectedActorSet.size());
			  for (Actor fromActor : fromActors)
			  {
				  Actor newActor = mLevelFile.cloneActorFromActor (fromActor);
				  
				  if (newActor != null)
					  newActors.add (newActor);
			  }
			  
			  if (newActors.size() > 0)
				  setSelectedActors(newActors, true);
			  
			  break;
		  }
		  default:
		  }
	  }
	  
	  // Method descriptor #10 (Ljava/awt/event/KeyEvent;)V
	  public void keyReleased(java.awt.event.KeyEvent e)
	  {
		  
	  }

//--------------------------------------------------------------------------
// as a FocusListener
//--------------------------------------------------------------------------

	  
	  // Method descriptor #9 (Ljava/awt/event/FocusEvent;)V
	  public void focusGained(java.awt.event.FocusEvent e)
	  {
		  return;
	  }
	  
	  // Method descriptor #9 (Ljava/awt/event/FocusEvent;)V
	  public void focusLost(java.awt.event.FocusEvent e)
	  {
		  return;
	  }	

}
